﻿//This filter is tested and seems to work correctly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This will create a virtual Grid, 
    /// then the average color is calculated of each square
    /// built by this grid.
    /// So the nXCount and nYCount is the resolution.
    /// Bigger values will refine the result.
    /// </summary>
    
    public enum REDUCINGMODE
    {
        PIXELLATE,     //pixellate
        AVERAGE,       //sets the average color value of the square as new square color
        SQUARE_CENTER, //sets the center of the square as new square color
        LEFT_TOP_EDGE,
        RIGHT_TOP_EDGE,
        LEFT_BOTTOM_EDGE,
        RIGHT_BOTTOM_EDGE
    }   

    /// <summary>
    /// This will pixellate.
    /// </summary>
    public class ColorReducingGrid : BaseImageFilter
    {
        protected int m_nXCount = 20;
        protected int m_nYCount = 20;
        protected REDUCINGMODE reducingmode = REDUCINGMODE.PIXELLATE;

        /// <summary>
        /// Default constructor
        /// </summary>
        public ColorReducingGrid()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_nDim"></param>
        public ColorReducingGrid(int _nDim)
            : this(REDUCINGMODE.PIXELLATE, _nDim, _nDim)
        {
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_colorReducingGrid"></param>
        internal ColorReducingGrid(ColorReducingGrid _colorReducingGrid)
        {
            this.m_nXCount = _colorReducingGrid.m_nXCount;
            this.m_nYCount = _colorReducingGrid.m_nYCount;
            this.reducingmode = _colorReducingGrid.reducingmode;
        }
        /// <summary>
        /// specify mode and steps directly
        /// </summary>
        /// <param name="_reducingmode"></param>
        /// <param name="_nXCount"></param>
        /// <param name="_nYCount"></param>
        public ColorReducingGrid(REDUCINGMODE _reducingmode, int _nXCount, int _nYCount)
        {
            m_nXCount = _nXCount;
            m_nYCount = _nYCount;
        }

        /// <summary>
        /// Gets or sets reducing mode
        /// </summary>
        public REDUCINGMODE REDUCINGMODE
        {
            get { return reducingmode; }
            set { reducingmode = value; }
        }

        /// <summary>
        /// count of squares in x direction
        /// </summary>
        public int XCount
        {
            get { return m_nXCount; }
            set { m_nXCount = value; }
        }

        /// <summary>
        /// count of squares in y direction
        /// </summary>
        public int YCount
        {
            get { return m_nYCount; }
            set { m_nYCount = value; }
        }

        /// <summary>
        /// Execute this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            //Divide Bitmap into Rectangles, virtually.
            int nXDim = _bitmap.Width / m_nXCount;
            int nYDim = _bitmap.Height / m_nYCount;

            Graphics g = Graphics.FromImage(_bitmap.InternalBitmap);

            Color colorOfSquare = Color.Transparent;

            Point ptPoint = Point.Empty;

            UnsafeGraphics graphics = new UnsafeGraphics(_bitmap);
            int y = 0;
            int x = 0;
            for (y = 0; y < m_nYCount; y++)
            {
                for (x = 0; x < m_nXCount; x++)
                {
                    Rectangle rect = new Rectangle(x * nXDim, y * nYDim, nXDim, nYDim);

                    switch (reducingmode)
                    {
                        case REDUCINGMODE.SQUARE_CENTER:
                            ptPoint = RectangleHelpers.Center(rect);
                            colorOfSquare = _bitmap.GetPixel(ptPoint.X, ptPoint.Y);
                            break;

                        case REDUCINGMODE.RIGHT_BOTTOM_EDGE:
                            ptPoint = new Point(rect.Right, rect.Bottom);
                            break;

                        case REDUCINGMODE.RIGHT_TOP_EDGE:
                            ptPoint = new Point(rect.Right, rect.Top);
                            break;

                        case REDUCINGMODE.LEFT_BOTTOM_EDGE:
                            ptPoint = new Point(rect.Left, rect.Bottom);
                            break;

                        case REDUCINGMODE.LEFT_TOP_EDGE:
                        case REDUCINGMODE.PIXELLATE:
                            ptPoint = new Point(rect.Left, rect.Top);
                            break;

                        case REDUCINGMODE.AVERAGE:
                            colorOfSquare = SquareColor(_bitmap, rect);
                            graphics.FillRectangle(new SolidBrush(colorOfSquare), rect);
                            break;
                    }

                    if (reducingmode != REDUCINGMODE.AVERAGE)
                    {
                        graphics.FillRectangle(new SolidBrush(_bitmap.GetPixel(ptPoint.X, ptPoint.Y)), rect);
                    }
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Calculate square color
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_rect"></param>
        /// <returns></returns>
        public Color SquareColor(UnsafeBitmap _bitmap, Rectangle _rect)
        {
            List<Color> aColor = new List<Color>();
            for(int y = _rect.Y; y < _rect.Bottom; y++)
            {
                for (int x = _rect.X; x < _rect.Right; x++)
                {
                    aColor.Add(_bitmap.GetPixel(x, y));
                }
            }
            return ColorFunctions.AverageColor(aColor);
        }

        /// <summary>
        /// Creates a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ColorReducingGrid(this.reducingmode, this.m_nXCount, this.m_nYCount);
        }
    }
}
